<?php
/**
 * Created by 无所谓已
 * ScreenshotService.php
 * Time: 2025/08/27 16:38
 * Updated: 2025/01/27 - 添加中文字幕截图支持
 */

namespace app\service;

class ScreenshotService
{
    /**
     * @var SubtitleDetector 字幕检测器
     */
    private $subtitleDetector;

    /**
     * @var OCRValidator OCR验证器
     */
    private $ocrValidator;

    public function __construct()
    {
        $this->subtitleDetector = new SubtitleDetector();
        $this->ocrValidator = new OCRValidator();
    }
    public function cleanImg()
    {
        $this->cleanupOldTempFiles(0);
        return true;
    }

    public function cleanupOldTempFiles($maxAge = 3600)
    {
        $tempPath = runtime_path() . '/temp/';
        if (!is_dir($tempPath)) {
            return;
        }

        $files = glob($tempPath . '*');
        $now = time();

        foreach ($files as $file) {
            if (is_file($file) && ($now - filemtime($file)) > $maxAge) {
                @unlink($file);
            }
        }
    }

    /**
     * 生成截图并上传到pixhost
     *
     * @param string $fileUrl 文件地址
     * @param string $mode    截图方式: single(单张), grid(九宫格)
     * @param string $format  图片格式: png, jpg
     * @param int    $count   单张模式下截图数量
     * @return array
     */
    public function captureAndUpload($fileUrl, $mode = 'single', $format = 'png', $count = 1)
    {
        try {
            // 判断是否为本地文件路径
            if (file_exists($fileUrl)) {
                $tempFile = $fileUrl;
            } else {
                // 下载文件到临时目录
                $tempFile = $this->downloadFile($fileUrl);
                if (!$tempFile) {
                    return ['code' => 1, 'msg' => '文件下载失败', 'data' => array(
                        'fileUrl' => $fileUrl,
                        'filestatus' => file_exists($fileUrl)
                    )];
                }
            }

            // 生成截图
            $screenshots = $this->generateScreenshots($tempFile, $mode, $format, $count);
            if (empty($screenshots)) {
                return ['code' => 1, 'msg' => '截图生成失败'];
            }
//            return $screenshots;

            // 上传到pixhost
            $uploadResults = [];
            $screenshotDetails = [];

            foreach ($screenshots as $index => $screenshotInfo) {
                $screenshotFile = is_array($screenshotInfo) ? $screenshotInfo['file'] : $screenshotInfo;

                // 检查文件是否存在
                if (!file_exists($screenshotFile)) {
                    $uploadResults[] = ['code' => 1, 'msg' => '截图文件不存在: ' . $screenshotFile];
                    continue;
                }

                // 检查文件大小是否超过10MB
                if (filesize($screenshotFile) > 10 * 1024 * 1024) {
                    // 如果超过10MB，尝试压缩
                    $compressed = $this->compressImage($screenshotFile, $format);
                    if ($compressed && filesize($compressed) <= 10 * 1024 * 1024) {
                        $result = $this->uploadToPixhost($compressed);
                    } else {
                        $result = ['code' => 1, 'msg' => '图片文件超过10MB限制'];
                    }
                } else {
                    $result = $this->uploadToPixhost($screenshotFile);
                }
                $uploadResults[] = $this->processPixhostUrl($result);

                // 保存截图详细信息
                if (is_array($screenshotInfo)) {
                    $screenshotDetails[] = [
                        'file_info' => $result,
                        'timestamp' => $screenshotInfo['timestamp'] ?? 0,
                        'time_formatted' => $screenshotInfo['time_formatted'] ?? '00:00',
                        'time_full' => $screenshotInfo['time_full'] ?? '00:00:00.00'
                    ];
                }
            }

            // 清理临时文件（仅当是下载的文件时才清理）
            if (!file_exists($fileUrl)) {
                @unlink($tempFile);
            }

            foreach ($screenshots as $screenshotInfo) {
                $screenshotFile = is_array($screenshotInfo) ? $screenshotInfo['file'] : $screenshotInfo;
                if (file_exists($screenshotFile) && $screenshotFile !== $fileUrl) {
                    @unlink($screenshotFile);
                }
            }
            $this->cleanupOldTempFiles();
            return [
                'code' => 0,
                'msg' => '成功',
                'data' => $uploadResults,
                'details' => $screenshotDetails
            ];
        } catch (\Exception $e) {
            $this->cleanupOldTempFiles();
            return [
                'code' => 1,
                'msg' => '处理失败: ' . $e->getMessage(),
                'data' => array(
                    'line' => $e->getLine(),
                    'file' => $e->getFile(),
                    'trace' => $e->getTraceAsString()
                )
            ];
        }
    }

    /**
     * 下载文件到临时目录
     */
    private function downloadFile($url)
    {
        $tempPath = runtime_path() . '/temp/';
        if (!is_dir($tempPath)) {
            mkdir($tempPath, 0755, true);
        }

        // 获取文件名
        $parsedUrl = parse_url($url);
        $pathInfo = pathinfo($parsedUrl['path']);
        $filename = uniqid() . '_' . ($pathInfo['filename'] ?? 'file') . '.' . ($pathInfo['extension'] ?? 'tmp');
        $tempFile = $tempPath . $filename;

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 300);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');

        $data = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode == 200 && $data) {
            file_put_contents($tempFile, $data);
            return $tempFile;
        }

        return false;
    }

    /**
     * 生成截图
     */
    private function generateScreenshots($file, $mode, $format, $count = 1)
    {
        $tempPath = runtime_path() . '/temp/';
        if (!is_dir($tempPath)) {
            mkdir($tempPath, 0755, true);
        }

        $screenshots = [];
        $format = strtolower($format);

        // 确保格式是支持的类型
        if (!in_array($format, ['png', 'jpg', 'jpeg'])) {
            $format = 'png';
        }

        // 确保count在合理范围内
        $count = max(1, min(20, (int)$count)); // 限制在1-20之间

        // 检查文件是否存在
        if (!file_exists($file)) {
            throw new \Exception('文件不存在: ' . $file . ' at line ' . __LINE__);
        }

        // 检查文件类型（简化处理，实际应使用更精确的方法）
        $isVideo = $this->isVideoFile($file);

        if ($mode === 'single') {
            if ($isVideo) {
                // 检测字幕信息
                $subtitleInfo = $this->subtitleDetector->detectSubtitles($file);
                $bestSubtitle = $this->subtitleDetector->getBestSubtitleForScreenshot($subtitleInfo);
                
                // 判断是否使用ffmpeg字幕截图（优先使用ffmpeg字幕滤镜）
                // 判断是否使用mpv截图（ffmpeg不支持时作为备选）
                $useFfmpegSubtitle = $this->shouldUseFfmpegSubtitle($subtitleInfo, $bestSubtitle);
                $useMpv = !$useFfmpegSubtitle && $this->shouldUseMpv($subtitleInfo, $bestSubtitle);

                // 生成指定数量的截图
                $duration = $this->getVideoDuration($file);

                // 计算有效时间范围（避开开头和结尾的10%）
                $startPoint = $duration * 0.1;
                $endPoint = $duration * 0.9;

                // 平均分布截图时间点
                $timestamps = [];
                if ($count == 1) {
                    // 如果只要一张截图，取中间点
                    $timestamps[] = ($startPoint + $endPoint) / 2;
                } else {
                    // 多张截图平均分布在整个有效时间范围内
                    for ($i = 0; $i < $count; $i++) {
                        $progress = $i / ($count - 1); // 从0到1的进度
                        $timestamps[] = $startPoint + ($endPoint - $startPoint) * $progress;
                    }
                }

                // 生成截图
                for ($i = 0; $i < count($timestamps); $i++) {
                    $timestamp = intval($timestamps[$i]);
                    $minutes = intval($timestamp / 60);
                    $seconds = $timestamp % 60;

                    // 修复：将浮点数转换为整数用于 gmdate 函数
                    $intTimestamp = (int)$timestamp;
                    $milliseconds = (int)(($timestamp - $intTimestamp) * 100);
                    $timeFormatted = gmdate("H:i:s", $intTimestamp) . '.' . sprintf("%02d", $milliseconds);
                    $outputFile = $tempPath . uniqid() . "_{$i}." . $format;

                    // 根据是否有中文字幕选择截图方式
                    if ($useFfmpegSubtitle) {
                        // 使用ffmpeg字幕滤镜截图（优先方案）
                        $this->captureWithFfmpegSubtitle($file, $timeFormatted, $outputFile, $bestSubtitle);
                    } elseif ($useMpv) {
                        // 使用mpv截图（备选方案）
                        $this->captureWithMpv($file, $timeFormatted, $outputFile, $bestSubtitle);
                    } else {
                        // 使用ffmpeg普通截图（无字幕）
                        // 对于MKV文件，使用更兼容的参数
                        $cmd = $this->buildFfmpegCommand($file, $timeFormatted, $outputFile);
                        exec($cmd, $output, $returnCode);

                        // 错误处理和调试信息
                        if ($returnCode !== 0) {
                            // 如果第一次失败，尝试使用更兼容的参数（针对MKV等格式）
                            $cmd = $this->buildFfmpegCommand($file, $timeFormatted, $outputFile, true);
                            exec($cmd, $output, $returnCode);
                            
                            if ($returnCode !== 0) {
                                $errorMsg = 'FFmpeg执行失败 at line ' . __LINE__ . ': ' . $cmd . "\n";
                                $errorMsg .= '返回码: ' . $returnCode . "\n";
                                $errorMsg .= '输出信息: ' . implode("\n", $output);
                                throw new \Exception($errorMsg);
                            }
                        }
                    }

                    if (file_exists($outputFile)) {
                        // OCR验证（如果OCR可用且检测到有中文字幕）
                        $ocrResult = null;
                        if (($useFfmpegSubtitle || $useMpv) && $subtitleInfo['has_chinese_subtitle']) {
                            $ocrResult = $this->validateWithOCR($outputFile);
                        }

                        $screenshots[] = [
                            'file' => $outputFile,
                            'timestamp' => $timestamp,
                            'time_formatted' => sprintf("%02d:%02d", $minutes, $seconds),
                            'time_full' => $timeFormatted,
                            'video_duration' => $duration,
                            'has_subtitle' => $subtitleInfo['has_chinese_subtitle'] ?? false,
                            'ocr_validated' => $ocrResult !== null ? $ocrResult['has_chinese'] : null,
                            'ocr_confidence' => $ocrResult !== null ? $ocrResult['confidence'] : null
                        ];
                    } else {
                        throw new \Exception('截图文件未生成 at line ' . __LINE__ . ': ' . $outputFile);
                    }
                }
            } else {
                // 对于图片文件，单张模式返回原图的副本
                for ($i = 0; $i < $count; $i++) {
                    $outputFile = $tempPath . uniqid() . "_{$i}." . $format;
                    if (!copy($file, $outputFile)) {
                        throw new \Exception('无法复制文件 at line ' . __LINE__ . ': from ' . $file . ' to ' . $outputFile);
                    }
                    $screenshots[] = [
                        'file' => $outputFile,
                        'timestamp' => 0,
                        'time_formatted' => '00:00',
                        'time_full' => '00:00:00.00'
                    ];
                }
            }
        } else if ($mode === 'grid') {
            if ($isVideo) {
                // 检测字幕信息
                $subtitleInfo = $this->subtitleDetector->detectSubtitles($file);
                $bestSubtitle = $this->subtitleDetector->getBestSubtitleForScreenshot($subtitleInfo);
                
                // 判断是否使用ffmpeg字幕截图（优先使用ffmpeg字幕滤镜）
                // 判断是否使用mpv截图（ffmpeg不支持时作为备选）
                $useFfmpegSubtitle = $this->shouldUseFfmpegSubtitle($subtitleInfo, $bestSubtitle);
                $useMpv = !$useFfmpegSubtitle && $this->shouldUseMpv($subtitleInfo, $bestSubtitle);

                // 生成九宫格截图（仅对视频文件）
                $duration = $this->getVideoDuration($file);

                // 计算有效时间范围（避开开头和结尾的10%）
                $startPoint = $duration * 0.1;
                $endPoint = $duration * 0.9;

                // 平均分布9个时间点
                $timestamps = [];
                for ($i = 0; $i < 9; $i++) {
                    $progress = $i / 8; // 从0到1的进度
                    $timestamps[] = $startPoint + ($endPoint - $startPoint) * $progress;
                }

                $imageFiles = [];
                for ($i = 0; $i < 9; $i++) {
                    $timestamp = intval($timestamps[$i]);
                    $minutes = floor($timestamp / 60);
                    $seconds = $timestamp % 60;

                    // 修复：将浮点数转换为整数用于 gmdate 函数
                    $intTimestamp = (int)$timestamp;
                    $milliseconds = (int)(($timestamp - $intTimestamp) * 100);
                    $timeFormatted = gmdate("H:i:s", $intTimestamp) . '.' . sprintf("%02d", $milliseconds);
                    $outputFile = $tempPath . uniqid() . "_{$i}." . $format;

                    // 根据是否有中文字幕选择截图方式
                    if ($useFfmpegSubtitle) {
                        // 使用ffmpeg字幕滤镜截图（优先方案）
                        $this->captureWithFfmpegSubtitle($file, $timeFormatted, $outputFile, $bestSubtitle);
                    } elseif ($useMpv) {
                        // 使用mpv截图（备选方案）
                        $this->captureWithMpv($file, $timeFormatted, $outputFile, $bestSubtitle);
                    } else {
                        // 使用ffmpeg普通截图（无字幕）
                        // 对于MKV文件，使用更兼容的参数
                        $cmd = $this->buildFfmpegCommand($file, $timeFormatted, $outputFile, false, true);
                        exec($cmd, $output, $returnCode);

                        // 错误处理和调试信息
                        if ($returnCode !== 0) {
                            // 如果第一次失败，尝试使用更兼容的参数（针对MKV等格式）
                            $cmd = $this->buildFfmpegCommand($file, $timeFormatted, $outputFile, true, true);
                            exec($cmd, $output, $returnCode);
                            
                            if ($returnCode !== 0) {
                                $errorMsg = 'FFmpeg执行失败 at line ' . __LINE__ . ': ' . $cmd . "\n";
                                $errorMsg .= '返回码: ' . $returnCode . "\n";
                                $errorMsg .= '输出信息: ' . implode("\n", $output);
                                throw new \Exception($errorMsg);
                            }
                        }
                    }

                    if (file_exists($outputFile)) {
                        // OCR验证（如果OCR可用且检测到有中文字幕）
                        $ocrResult = null;
                        if (($useFfmpegSubtitle || $useMpv) && $subtitleInfo['has_chinese_subtitle']) {
                            $ocrResult = $this->validateWithOCR($outputFile);
                        }

                        $imageFiles[] = [
                            'file' => $outputFile,
                            'timestamp' => $timestamp,
                            'time_formatted' => sprintf("%02d:%02d", $minutes, $seconds),
                            'time_full' => $timeFormatted,
                            'video_duration' => $duration,
                            'has_subtitle' => $subtitleInfo['has_chinese_subtitle'] ?? false,
                            'ocr_validated' => $ocrResult !== null ? $ocrResult['has_chinese'] : null,
                            'ocr_confidence' => $ocrResult !== null ? $ocrResult['confidence'] : null
                        ];
                    } else {
                        throw new \Exception('截图文件未生成 at line ' . __LINE__ . ': ' . $outputFile);
                    }
                }

                // 合并成九宫格
                if (count($imageFiles) == 9) {
                    $gridFile = $tempPath . uniqid() . '_grid.' . $format;

                    // 提取文件路径用于合并
                    $filesToMerge = array_map(function ($item) {
                        return $item['file'];
                    }, $imageFiles);

                    // 使用引号包裹所有文件路径
                    $filesQuoted = array_map(function ($file) {
                        return '"' . $file . '"';
                    }, $filesToMerge);

                    // 保持原始图片尺寸进行合并
                    $cmd = "montage " . implode(' ', $filesQuoted) .
                        " -geometry +2+2 -tile 3x3 -quality 80 -strip -background none \"" . $gridFile . "\" 2>&1";

                    // 执行命令并捕获输出
                    exec($cmd, $output, $returnCode);

                    // 错误处理和调试信息
                    if ($returnCode === 0 && file_exists($gridFile)) {
                        // 清理单张截图，只保留九宫格
                        foreach ($filesToMerge as $shot) {
                            @unlink($shot);
                        }

                        // 构建九宫格截图信息
                        $gridInfo = [
                            'file' => $gridFile,
                            'timestamp' => array_column($imageFiles, 'timestamp'),
                            'time_formatted' => array_column($imageFiles, 'time_formatted'),
                            'time_full' => array_column($imageFiles, 'time_full'),
                            'video_duration' => $duration
                        ];
                        $screenshots[] = $gridInfo;
                    } else {
                        // 合并失败时应该抛出异常而不是返回单张图片
                        $errorMsg = '九宫格合并失败 at line ' . __LINE__ . ': ' . $cmd . "\n";
                        $errorMsg .= '返回码: ' . $returnCode . "\n";
                        $errorMsg .= '输出信息: ' . implode("\n", $output);
                        throw new \Exception($errorMsg); // 修改这里，抛出异常而不是继续执行
                    }
                } else {
                    $screenshots = $imageFiles;
                }
            } else {
                // 对于图片文件，九宫格模式返回原图
                $outputFile = $tempPath . uniqid() . '_original.' . $format;
                if (!copy($file, $outputFile)) {
                    throw new \Exception('无法复制文件 at line ' . __LINE__ . ': from ' . $file . ' to ' . $outputFile);
                }
                $screenshots[] = [
                    'file' => $outputFile,
                    'timestamp' => 0,
                    'time_formatted' => '00:00',
                    'time_full' => '00:00:00.00'
                ];
            }
        }

        return $screenshots;
    }

    /**
     * 判断是否为视频文件（简化版）
     */
    private function isVideoFile($file)
    {
        $videoExtensions = ['mp4', 'avi', 'mkv', 'mov', 'wmv', 'flv', 'webm', 'm4v', 'mpeg', 'mpg', 'ts', 'm2ts', 'mts', 'm2t'];
        $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
        return in_array($extension, $videoExtensions);
    }

    private function getVideoDuration($file)
    {
        // 使用引号包裹文件路径
        $cmd = "ffprobe -v quiet -show_entries format=duration -of csv=p=0 \"" . $file . "\" 2>&1";
        $duration = exec($cmd, $output, $returnCode);

        // 检查命令是否执行成功
        if ($returnCode !== 0) {
            $errorMsg = 'FFprobe执行失败 at line ' . __LINE__ . ': ' . $cmd . "\n";
            $errorMsg .= '返回码: ' . $returnCode . "\n";
            $errorMsg .= '输出信息: ' . implode("\n", $output);
            throw new \Exception($errorMsg);
        }

        // 清理和验证返回值
        $duration = trim($duration);
        if (is_numeric($duration) && $duration > 0) {
            return (float)$duration;
        }

        // 如果无法获取时长，尝试其他方法
        $cmd = "ffprobe -v quiet -show_entries format=duration -of default=nw=1 \"" . $file . "\" 2>&1";
        $duration = exec($cmd, $output, $returnCode);
        $duration = trim($duration);

        if ($returnCode === 0 && is_numeric($duration) && $duration > 0) {
            return (float)$duration;
        }

        // 如果还是无法获取，抛出异常
        $errorMsg = '无法获取视频时长 at line ' . __LINE__ . "\n";
        $errorMsg .= 'ffprobe命令: ' . $cmd . "\n";
        $errorMsg .= 'ffprobe输出: ' . implode("\n", $output);
        throw new \Exception($errorMsg);
    }

    /**
     * 压缩图片以满足大小要求
     */
    private function compressImage($imageFile, $format)
    {
        $tempPath = runtime_path() . '/temp/';
        $compressedFile = $tempPath . uniqid() . '_compressed.' . $format;

        // 使用imagemagick压缩图片
        $cmd = "convert {$imageFile} -quality 80 -resize 800x600> {$compressedFile} 2>&1";
        exec($cmd, $output, $returnCode);

        if ($returnCode === 0 && file_exists($compressedFile)) {
            // 如果还是太大，进一步压缩
            if (filesize($compressedFile) > 10 * 1024 * 1024) {
                $cmd = "convert {$imageFile} -quality 60 -resize 640x480> {$compressedFile} 2>&1";
                exec($cmd, $output, $returnCode);

                if ($returnCode === 0 && file_exists($compressedFile)) {
                    return $compressedFile;
                }
            } else {
                return $compressedFile;
            }
        }

        return false;
    }

    /**
     * 上传到pixhost (使用官方API)
     */
    private function uploadToPixhost($imageFile)
    {
        $url = 'https://api.pixhost.to/images';

        // 检查文件是否存在
        if (!file_exists($imageFile)) {
            return [
                'code' => 1,
                'msg' => '上传文件不存在: ' . $imageFile,
                'file' => basename($imageFile)
            ];
        }

        // 检查文件大小
        $fileSize = filesize($imageFile);
        if ($fileSize > 10 * 1024 * 1024) { // 10MB
            return [
                'code' => 1,
                'msg' => '文件大小超过限制 (10MB)',
                'file' => basename($imageFile),
                'size' => $fileSize
            ];
        }

        // 使用cURL的CURLFile接口上传文件
        $postFields = [
            'img' => new \CURLFile($imageFile),
            'content_type' => '0', // 0 = FS (Family Safe), 1 = NSFW
            'max_th_size' => '300' // 设置缩略图大小为300px
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Accept: application/json',
            'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            'Referer: https://pixhost.to/'
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 300);
        curl_setopt($ch, CURLOPT_HEADER, true); // 获取响应头信息
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 忽略SSL证书验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 忽略SSL主机名验证

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $headers = substr($response, 0, $headerSize);
        $body = substr($response, $headerSize);

        curl_close($ch);

        // 记录调试信息
        error_log("Pixhost API 上传请求 - HTTP状态码: " . $httpCode);
        error_log("Pixhost API 上传请求 - 响应头: " . $headers);
        error_log("Pixhost API 上传请求 - 响应体: " . $body);

        if ($httpCode == 200) {
            $result = json_decode($body, true);
            if (isset($result['show_url'])) {
                return [
                    'code' => 0,
                    'url' => $result['show_url'],
                    'thumb' => $result['th_url'] ?? '',
                    'file' => basename($imageFile),
                    'size' => $fileSize
                ];
            } else {
                return [
                    'code' => 1,
                    'msg' => '上传失败: ' . $body,
                    'file' => basename($imageFile),
                    'response' => $result
                ];
            }
        }

        // 处理API错误响应
        if ($httpCode >= 400) {
            $errorMsg = 'API错误响应 - HTTP状态码: ' . $httpCode;
            if ($body) {
                $errorMsg .= ' - 响应内容: ' . $body;
            }
            return [
                'code' => 1,
                'msg' => $errorMsg,
                'file' => basename($imageFile),
                'details' => [
                    'headers' => $headers,
                    'body' => $body
                ]
            ];
        }

        return [
            'code' => 1,
            'msg' => '上传请求失败，HTTP状态码: ' . $httpCode,
            'file' => basename($imageFile),
            'details' => [
                'headers' => $headers,
                'body' => $body
            ]
        ];
    }

    public function processPixhostUrl($uploadResult)
    {
        // 检查是否是成功的上传结果
        if ($uploadResult['code'] === 0 && isset($uploadResult['url']) && isset($uploadResult['thumb'])) {
            // 提取缩略图URL中的子域名前缀数字
            $thumbUrl = $uploadResult['thumb'];
            $pattern = '/https:\/\/t(\d+)\.pixhost\.to/';
            preg_match($pattern, $thumbUrl, $matches);

            if (!empty($matches)) {
                $number = $matches[1];
                // 替换URL中的域名部分
                $newUrl = str_replace('https://pixhost.to/show/', "https://img{$number}.pixhost.to/images/", $uploadResult['url']);

                // 更新返回结果中的URL
                $uploadResult['direct_url'] = $newUrl;
            }
        }

        return $uploadResult;
    }

    /**
     * 判断是否应该使用ffmpeg字幕滤镜进行截图
     *
     * @param array $subtitleInfo 字幕检测结果
     * @param array|null $bestSubtitle 最佳字幕信息
     * @return bool
     */
    private function shouldUseFfmpegSubtitle($subtitleInfo, $bestSubtitle)
    {
        // 如果没有字幕，不使用字幕滤镜
        if (!$subtitleInfo['has_subtitle'] || $bestSubtitle === null) {
            return false;
        }

        // 优先使用ffmpeg字幕滤镜处理中文字幕
        if ($subtitleInfo['has_chinese_subtitle']) {
            // 检查字幕类型，ffmpeg支持内置字幕流和外部字幕文件
            if ($bestSubtitle['type'] === 'internal' || $bestSubtitle['type'] === 'external') {
                return true;
            }
        }

        return false;
    }

    /**
     * 判断是否应该使用mpv进行截图
     *
     * @param array $subtitleInfo 字幕检测结果
     * @param array|null $bestSubtitle 最佳字幕信息
     * @return bool
     */
    private function shouldUseMpv($subtitleInfo, $bestSubtitle)
    {
        // 如果没有字幕，不使用mpv
        if (!$subtitleInfo['has_subtitle'] || $bestSubtitle === null) {
            return false;
        }

        // 优先使用mpv处理中文字幕
        if ($subtitleInfo['has_chinese_subtitle']) {
            // 检查mpv是否可用
            return $this->isMpvAvailable();
        }

        return false;
    }

    /**
     * 检查mpv是否可用
     *
     * @return bool
     */
    private function isMpvAvailable()
    {
        static $mpvAvailable = null;
        
        if ($mpvAvailable !== null) {
            return $mpvAvailable;
        }

        // 检查mpv命令是否存在
        $cmd = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' 
            ? 'where mpv 2>nul' 
            : 'which mpv 2>/dev/null';
        
        exec($cmd, $output, $returnCode);
        $mpvAvailable = ($returnCode === 0);
        
        return $mpvAvailable;
    }

    /**
     * 使用ffmpeg字幕滤镜进行截图（包含字幕）
     *
     * @param string $videoFile 视频文件路径
     * @param string $time 时间点（格式：HH:MM:SS.mm）
     * @param string $outputFile 输出文件路径
     * @param array|null $subtitleInfo 字幕信息
     * @throws \Exception
     */
    private function captureWithFfmpegSubtitle($videoFile, $time, $outputFile, $subtitleInfo = null)
    {
        $vfFilter = '';
        
        if ($subtitleInfo && $subtitleInfo['type'] === 'external' && isset($subtitleInfo['file'])) {
            // 外部字幕文件
            $subtitleFile = $subtitleInfo['file'];
            $extension = strtolower(pathinfo($subtitleFile, PATHINFO_EXTENSION));
            
            // 使用引号包裹路径，处理特殊字符
            $subtitleFileEscaped = str_replace('\\', '\\\\', $subtitleFile);
            $subtitleFileEscaped = str_replace(':', '\\:', $subtitleFileEscaped);
            $subtitleFileEscaped = str_replace('[', '\\[', $subtitleFileEscaped);
            $subtitleFileEscaped = str_replace(']', '\\]', $subtitleFileEscaped);
            
            if ($extension === 'ass' || $extension === 'ssa') {
                // ASS/SSA 字幕使用 ass 滤镜
                $vfFilter = 'ass=' . escapeshellarg($subtitleFile);
            } else {
                // SRT/VTT 等使用 subtitles 滤镜
                $vfFilter = 'subtitles=' . escapeshellarg($subtitleFile);
            }
        } elseif ($subtitleInfo && $subtitleInfo['type'] === 'internal' && isset($subtitleInfo['stream']['index'])) {
            // 内置字幕流，使用 subtitles 滤镜指定字幕轨道
            // 使用引号包裹视频文件路径
            $videoFileEscaped = str_replace('\\', '\\\\', $videoFile);
            $videoFileEscaped = str_replace(':', '\\:', $videoFileEscaped);
            $videoFileEscaped = str_replace('[', '\\[', $videoFileEscaped);
            $videoFileEscaped = str_replace(']', '\\]', $videoFileEscaped);
            
            $vfFilter = 'subtitles=' . escapeshellarg($videoFile) . ':si=' . $subtitleInfo['stream']['index'];
        }
        
        if (empty($vfFilter)) {
            // 如果没有字幕滤镜，降级到普通截图
            $cmd = $this->buildFfmpegCommand($videoFile, $time, $outputFile);
        } else {
            // 使用字幕滤镜进行截图（先使用标准模式）
            $cmd = $this->buildFfmpegCommandWithFilter($videoFile, $time, $outputFile, $vfFilter);
        }
        
        exec($cmd, $output, $returnCode);
        
        if ($returnCode !== 0) {
            // 如果字幕滤镜失败，先尝试兼容模式
            if (!empty($vfFilter)) {
                $errorMsg = 'FFmpeg字幕滤镜执行失败: ' . implode("\n", $output);
                $cmd = $this->buildFfmpegCommandWithFilter($videoFile, $time, $outputFile, $vfFilter, true);
                exec($cmd, $output, $returnCode);
                
                if ($returnCode !== 0) {
                    // 兼容模式也失败，降级到普通截图
                    $cmd = $this->buildFfmpegCommand($videoFile, $time, $outputFile, true);
                    exec($cmd, $output, $returnCode);
                    
                    if ($returnCode !== 0 || !file_exists($outputFile)) {
                        throw new \Exception($errorMsg . "\n降级到普通截图也失败");
                    }
                    // 降级成功，继续执行
                }
            } else {
                // 普通截图失败，尝试兼容模式
                $errorMsg = 'FFmpeg执行失败: ' . implode("\n", $output);
                $cmd = $this->buildFfmpegCommand($videoFile, $time, $outputFile, true);
                exec($cmd, $output, $returnCode);
                
                if ($returnCode !== 0 || !file_exists($outputFile)) {
                    throw new \Exception($errorMsg . "\n兼容模式也失败");
                }
            }
        }
        
        // 检查输出文件是否存在
        if (!file_exists($outputFile)) {
            throw new \Exception('FFmpeg字幕截图文件未生成: ' . $outputFile);
        }
    }

    /**
     * 使用mpv进行截图（包含字幕）
     *
     * @param string $videoFile 视频文件路径
     * @param string $time 时间点（格式：HH:MM:SS.mm）
     * @param string $outputFile 输出文件路径
     * @param array|null $subtitleInfo 字幕信息
     * @throws \Exception
     */
    private function captureWithMpv($videoFile, $time, $outputFile, $subtitleInfo = null)
    {
        // 将时间格式转换为秒数（mpv的--start参数接受秒数或HH:MM:SS格式）
        // mpv支持HH:MM:SS格式，直接使用
        $timeValue = $time;
        
        // 获取输出文件扩展名
        $extension = strtolower(pathinfo($outputFile, PATHINFO_EXTENSION));
        $format = ($extension === 'jpg' || $extension === 'jpeg') ? 'jpg' : 'png';
        
        // 创建临时lua脚本用于自动截图
        $tempPath = runtime_path() . '/temp/';
        if (!is_dir($tempPath)) {
            mkdir($tempPath, 0755, true);
        }
        $luaScript = $tempPath . 'mpv_screenshot_' . uniqid() . '.lua';
        
        // 创建lua脚本内容：在播放第一帧时自动截图
        // 使用on_load事件在文件加载后立即截图
        // 使用outputFile作为截图输出路径
        $outputFileEscaped = str_replace('\\', '\\\\', $outputFile);
        $outputFileEscaped = str_replace('"', '\\"', $outputFileEscaped);
        $luaContent = "mp.register_event('file-loaded', function()\n" .
                      "    mp.add_timeout(0.1, function()\n" .
                      "        mp.command('screenshot-to-file \"' .. '" . $outputFileEscaped . "' .. '\"')\n" .
                      "        mp.command('quit')\n" .
                      "    end)\n" .
                      "end)\n";
        file_put_contents($luaScript, $luaContent);
        
        // 构建mpv命令
        // 使用--frames=1只播放1帧，--start指定开始时间
        $cmdParts = [
            'mpv',
            '--frames=1',
            '--start=' . $timeValue,
            '--screenshot-format=' . $format,
            '--screenshot-template="' . $outputFile . '"',
            '--script="' . $luaScript . '"',
            '--no-audio',
            '--no-osc',  // 不显示OSD
            '--no-osd-bar',  // 不显示进度条
            '--vo=null',  // 使用null输出（不显示窗口）
            '--quiet',  // 静默模式
            '--really-quiet',  // 完全静默
            '--no-terminal',  // 不显示终端输出
        ];

        // 添加字幕相关参数
        if ($subtitleInfo && $subtitleInfo['type'] === 'external' && isset($subtitleInfo['file'])) {
            // 外部字幕文件
            $cmdParts[] = '--sub-file="' . $subtitleInfo['file'] . '"';
        } elseif ($subtitleInfo && $subtitleInfo['type'] === 'internal' && isset($subtitleInfo['stream']['index'])) {
            // 内置字幕流，指定字幕轨道
            $cmdParts[] = '--sid=' . $subtitleInfo['stream']['index'];
        }

        // 添加字幕字体大小（可选，使用默认值）
        $cmdParts[] = '--sub-font-size=24';

        // 添加视频文件
        $cmdParts[] = '"' . $videoFile . '"';

        $cmd = implode(' ', $cmdParts) . ' 2>&1';

        // 执行命令
        exec($cmd, $output, $returnCode);

        // 错误处理
        if ($returnCode !== 0) {
            // 如果mpv失败，尝试使用ffmpeg作为降级方案
            $errorMsg = 'MPV执行失败 at line ' . __LINE__ . ': ' . $cmd . "\n";
            $errorMsg .= '返回码: ' . $returnCode . "\n";
            $errorMsg .= '输出信息: ' . implode("\n", $output);
            
            // 降级到ffmpeg
            $timeFormatted = $time;
            $ffmpegCmd = $this->buildFfmpegCommand($videoFile, $timeFormatted, $outputFile, true);
            exec($ffmpegCmd, $ffmpegOutput, $ffmpegReturnCode);
            
            if ($ffmpegReturnCode !== 0 || !file_exists($outputFile)) {
                throw new \Exception($errorMsg . "\n降级到FFmpeg也失败");
            }
            // ffmpeg成功，继续执行
        }

        // 清理临时lua脚本
        if (file_exists($luaScript)) {
            @unlink($luaScript);
        }

        // 检查输出文件是否存在
        if (!file_exists($outputFile)) {
            throw new \Exception('MPV截图文件未生成: ' . $outputFile);
        }
    }

    /**
     * 使用OCR验证截图是否包含中文字幕
     *
     * @param string $screenshotFile 截图文件路径
     * @return array|null OCR验证结果，如果OCR不可用返回null
     */
    private function validateWithOCR($screenshotFile)
    {
        // 检查OCR是否可用
        if (!$this->ocrValidator->isOCRAvailable()) {
            return null;
        }

        // 执行OCR验证
        return $this->ocrValidator->validateScreenshot($screenshotFile);
    }

    /**
     * 智能重拍：如果OCR未检测到字幕，重新选择时间点截图
     * 此方法用于single模式，当截图数量较少时可以尝试重拍
     *
     * @param string $videoFile 视频文件路径
     * @param float $duration 视频时长
     * @param string $format 图片格式
     * @param array $subtitleInfo 字幕信息
     * @param array|null $bestSubtitle 最佳字幕信息
     * @param int $maxRetries 最大重试次数
     * @return array|null 新的截图信息，失败返回null
     */
    private function retryScreenshotWithOCR($videoFile, $duration, $format, $subtitleInfo, $bestSubtitle, $maxRetries = 3)
    {
        // 检查OCR是否可用
        if (!$this->ocrValidator->isOCRAvailable()) {
            return null;
        }

        // 检查是否真的需要重拍（只有在有中文字幕但OCR未验证成功时才重拍）
        if (!$subtitleInfo['has_chinese_subtitle']) {
            return null;
        }

        $tempPath = runtime_path() . '/temp/';
        if (!is_dir($tempPath)) {
            mkdir($tempPath, 0755, true);
        }

        // 计算有效时间范围（避开开头和结尾的10%）
        $startPoint = $duration * 0.1;
        $endPoint = $duration * 0.9;

        // 尝试不同的时间点
        for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
            // 随机选择时间点（在有效范围内）
            $randomProgress = mt_rand(0, 100) / 100;
            $timestamp = $startPoint + ($endPoint - $startPoint) * $randomProgress;
            
            $minutes = intval($timestamp / 60);
            $seconds = $timestamp % 60;
            $intTimestamp = (int)$timestamp;
            $milliseconds = (int)(($timestamp - $intTimestamp) * 100);
            $timeFormatted = gmdate("H:i:s", $intTimestamp) . '.' . sprintf("%02d", $milliseconds);
            
            $outputFile = $tempPath . uniqid() . '_retry_' . $attempt . '.' . $format;

            try {
                // 生成截图
                // 优先使用ffmpeg字幕滤镜，其次mpv，最后普通截图
                $useFfmpegSubtitleRetry = $this->shouldUseFfmpegSubtitle($subtitleInfo, $bestSubtitle);
                $useMpvRetry = !$useFfmpegSubtitleRetry && $this->shouldUseMpv($subtitleInfo, $bestSubtitle);
                
                if ($useFfmpegSubtitleRetry) {
                    $this->captureWithFfmpegSubtitle($videoFile, $timeFormatted, $outputFile, $bestSubtitle);
                } elseif ($useMpvRetry) {
                    $this->captureWithMpv($videoFile, $timeFormatted, $outputFile, $bestSubtitle);
                } else {
                    $cmd = $this->buildFfmpegCommand($videoFile, $timeFormatted, $outputFile, true);
                    exec($cmd, $output, $returnCode);
                    if ($returnCode !== 0 || !file_exists($outputFile)) {
                        continue;
                    }
                }

                // OCR验证
                $ocrResult = $this->validateWithOCR($outputFile);
                
                if ($ocrResult && $ocrResult['has_chinese'] && $ocrResult['confidence'] > 0.3) {
                    // 找到包含中文字幕的截图
                    return [
                        'file' => $outputFile,
                        'timestamp' => $timestamp,
                        'time_formatted' => sprintf("%02d:%02d", $minutes, $seconds),
                        'time_full' => $timeFormatted,
                        'video_duration' => $duration,
                        'has_subtitle' => true,
                        'ocr_validated' => true,
                        'ocr_confidence' => $ocrResult['confidence']
                    ];
                }

                // 如果OCR验证失败，删除这个截图，继续尝试
                if (file_exists($outputFile)) {
                    @unlink($outputFile);
                }
            } catch (\Exception $e) {
                // 出错时删除文件，继续尝试
                if (file_exists($outputFile)) {
                    @unlink($outputFile);
                }
                continue;
            }
        }

        return null; // 重试失败
    }

    /**
     * 获取视频格式类型配置
     *
     * @param string $videoFile 视频文件路径
     * @return array 格式配置信息
     */
    private function getVideoFormatConfig($videoFile)
    {
        $extension = strtolower(pathinfo($videoFile, PATHINFO_EXTENSION));
        
        // 定义不同格式的配置
        $formatConfigs = [
            // MKV格式：需要兼容模式，使用精确时间定位
            'mkv' => [
                'compatible' => true,
                'precise_time' => true,
                'analyze_duration' => 10000000,
                'probe_size' => 10000000,
                'extra_params' => []
            ],
            // MP4格式：标准模式，性能优先
            'mp4' => [
                'compatible' => false,
                'precise_time' => false,
                'analyze_duration' => 0,
                'probe_size' => 0,
                'extra_params' => []
            ],
            // AVI格式：可能需要兼容模式
            'avi' => [
                'compatible' => true,
                'precise_time' => true,
                'analyze_duration' => 5000000,
                'probe_size' => 5000000,
                'extra_params' => []
            ],
            // MOV格式：标准模式
            'mov' => [
                'compatible' => false,
                'precise_time' => false,
                'analyze_duration' => 0,
                'probe_size' => 0,
                'extra_params' => []
            ],
            // WMV格式：需要兼容模式
            'wmv' => [
                'compatible' => true,
                'precise_time' => true,
                'analyze_duration' => 10000000,
                'probe_size' => 10000000,
                'extra_params' => []
            ],
            // FLV格式：标准模式
            'flv' => [
                'compatible' => false,
                'precise_time' => false,
                'analyze_duration' => 0,
                'probe_size' => 0,
                'extra_params' => []
            ],
            // WEBM格式：标准模式
            'webm' => [
                'compatible' => false,
                'precise_time' => false,
                'analyze_duration' => 0,
                'probe_size' => 0,
                'extra_params' => []
            ],
            // M4V格式：标准模式
            'm4v' => [
                'compatible' => false,
                'precise_time' => false,
                'analyze_duration' => 0,
                'probe_size' => 0,
                'extra_params' => []
            ],
            // MPEG/MPG格式：标准模式
            'mpeg' => [
                'compatible' => false,
                'precise_time' => false,
                'analyze_duration' => 0,
                'probe_size' => 0,
                'extra_params' => []
            ],
            'mpg' => [
                'compatible' => false,
                'precise_time' => false,
                'analyze_duration' => 0,
                'probe_size' => 0,
                'extra_params' => []
            ],
            // TS格式：需要兼容模式
            'ts' => [
                'compatible' => true,
                'precise_time' => true,
                'analyze_duration' => 10000000,
                'probe_size' => 10000000,
                'extra_params' => []
            ],
            // M2TS格式：需要兼容模式
            'm2ts' => [
                'compatible' => true,
                'precise_time' => true,
                'analyze_duration' => 10000000,
                'probe_size' => 10000000,
                'extra_params' => []
            ],
            // MTS格式：需要兼容模式
            'mts' => [
                'compatible' => true,
                'precise_time' => true,
                'analyze_duration' => 10000000,
                'probe_size' => 10000000,
                'extra_params' => []
            ],
            // M2T格式：需要兼容模式
            'm2t' => [
                'compatible' => true,
                'precise_time' => true,
                'analyze_duration' => 10000000,
                'probe_size' => 10000000,
                'extra_params' => []
            ],
        ];
        
        // 返回对应格式的配置，如果格式未知则使用兼容模式
        if (isset($formatConfigs[$extension])) {
            return $formatConfigs[$extension];
        }
        
        // 未知格式：使用兼容模式（安全起见）
        return [
            'compatible' => true,
            'precise_time' => true,
            'analyze_duration' => 10000000,
            'probe_size' => 10000000,
            'extra_params' => []
        ];
    }

    /**
     * 构建ffmpeg截图命令（根据视频格式智能选择参数）
     *
     * @param string $videoFile 视频文件路径
     * @param string $time 时间点（格式：HH:MM:SS.mm）
     * @param string $outputFile 输出文件路径
     * @param bool $forceCompatibleMode 强制使用兼容模式（用于重试）
     * @param bool $useUpdateFlag 是否使用-update参数（用于九宫格模式）
     * @return string ffmpeg命令
     */
    private function buildFfmpegCommand($videoFile, $time, $outputFile, $forceCompatibleMode = false, $useUpdateFlag = false)
    {
        // 获取格式配置
        $formatConfig = $this->getVideoFormatConfig($videoFile);
        
        // 如果强制兼容模式，覆盖配置
        if ($forceCompatibleMode) {
            $formatConfig['compatible'] = true;
            $formatConfig['precise_time'] = true;
            $formatConfig['analyze_duration'] = 10000000;
            $formatConfig['probe_size'] = 10000000;
        }
        
        $cmdParts = ['ffmpeg', '-y'];
        
        // 添加分析参数（如果需要）
        if ($formatConfig['analyze_duration'] > 0) {
            $cmdParts[] = '-analyzeduration ' . $formatConfig['analyze_duration'];
        }
        if ($formatConfig['probe_size'] > 0) {
            $cmdParts[] = '-probesize ' . $formatConfig['probe_size'];
        }
        
        // 根据格式选择时间定位方式
        if ($formatConfig['precise_time']) {
            // 精确时间模式：在输入前使用-ss快速定位，输入后再次使用-ss精确调整
            $cmdParts[] = '-ss ' . escapeshellarg($time);
            $cmdParts[] = '-i ' . escapeshellarg($videoFile);
            $cmdParts[] = '-ss 0'; // 精确调整
        } else {
            // 快速模式：只在输入前使用-ss（性能更好）
            $cmdParts[] = '-ss ' . escapeshellarg($time);
            $cmdParts[] = '-i ' . escapeshellarg($videoFile);
        }
        
        // 添加额外参数
        if (!empty($formatConfig['extra_params'])) {
            foreach ($formatConfig['extra_params'] as $param) {
                $cmdParts[] = $param;
            }
        }
        
        // 输出参数
        $cmdParts[] = '-vframes 1';
        if ($useUpdateFlag) {
            $cmdParts[] = '-update 1';
        }
        $cmdParts[] = '-q:v 2';
        $cmdParts[] = escapeshellarg($outputFile);
        
        return implode(' ', $cmdParts) . ' 2>&1';
    }

    /**
     * 构建带字幕滤镜的ffmpeg截图命令（根据视频格式智能选择参数）
     *
     * @param string $videoFile 视频文件路径
     * @param string $time 时间点（格式：HH:MM:SS.mm）
     * @param string $outputFile 输出文件路径
     * @param string $vfFilter 视频滤镜
     * @param bool $forceCompatibleMode 强制使用兼容模式（用于重试）
     * @return string ffmpeg命令
     */
    private function buildFfmpegCommandWithFilter($videoFile, $time, $outputFile, $vfFilter, $forceCompatibleMode = false)
    {
        // 获取格式配置
        $formatConfig = $this->getVideoFormatConfig($videoFile);
        
        // 如果强制兼容模式，覆盖配置
        if ($forceCompatibleMode) {
            $formatConfig['compatible'] = true;
            $formatConfig['precise_time'] = true;
            $formatConfig['analyze_duration'] = 10000000;
            $formatConfig['probe_size'] = 10000000;
        }
        
        $cmdParts = ['ffmpeg', '-y'];
        
        // 添加分析参数（如果需要）
        if ($formatConfig['analyze_duration'] > 0) {
            $cmdParts[] = '-analyzeduration ' . $formatConfig['analyze_duration'];
        }
        if ($formatConfig['probe_size'] > 0) {
            $cmdParts[] = '-probesize ' . $formatConfig['probe_size'];
        }
        
        // 根据格式选择时间定位方式
        if ($formatConfig['precise_time']) {
            // 精确时间模式
            $cmdParts[] = '-ss ' . escapeshellarg($time);
            $cmdParts[] = '-i ' . escapeshellarg($videoFile);
            $cmdParts[] = '-ss 0'; // 精确调整
        } else {
            // 快速模式
            $cmdParts[] = '-ss ' . escapeshellarg($time);
            $cmdParts[] = '-i ' . escapeshellarg($videoFile);
        }
        
        // 添加额外参数
        if (!empty($formatConfig['extra_params'])) {
            foreach ($formatConfig['extra_params'] as $param) {
                $cmdParts[] = $param;
            }
        }
        
        // 添加字幕滤镜
        $cmdParts[] = '-vf ' . escapeshellarg($vfFilter);
        
        // 输出参数
        $cmdParts[] = '-vframes 1';
        $cmdParts[] = '-q:v 2';
        $cmdParts[] = escapeshellarg($outputFile);
        
        return implode(' ', $cmdParts) . ' 2>&1';
    }
}